fix(code): reliable cloud message-queue dispatch + optimistic prompt UX#1905
Open
VojtechBartos wants to merge 5 commits intomainfrom
Open
fix(code): reliable cloud message-queue dispatch + optimistic prompt UX#1905VojtechBartos wants to merge 5 commits intomainfrom
VojtechBartos wants to merge 5 commits intomainfrom
Conversation
Contributor
Prompt To Fix All With AIThis is a comment left during a code review.
Path: apps/code/src/renderer/features/sessions/service/service.test.ts
Line: 834-890
Comment:
**Real-time path not covered by the new test**
The new test exercises only the log-replay path (via `mockConvertStoredEntriesToEvents`). The real-time path — where `handleSessionEvent` receives the `run_started` `AcpMessage` directly and calls `updatePromptStateFromEvents` — is not tested. If the handler in `updatePromptStateFromEvents` were accidentally removed or broken, no test would catch it.
A second `it` case (or parameterised variant) that calls `handleSessionEvent` with a crafted `run_started` `AcpMessage` and asserts `updateSession` is called with `{ status: "connected" }` would close this gap.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: apps/code/src/renderer/features/sessions/components/SessionView.tsx
Line: 602-613
Comment:
**Tooltip precedence when both `handoffInProgress` and `!isAgentReady` are true**
When `handoffInProgress` is `true` and `isAgentReady` is also `false`, `submitDisabledExternal` will be `true` but the tooltip will say "Waiting for agent to be ready…" rather than a handoff-related message. If `PromptInput` shows the `submitTooltipOverride` unconditionally when non-undefined, the user sees a potentially misleading message. Worth verifying whether these two conditions are mutually exclusive in practice, or if handoff tooltip priority should be explicit.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "chore(code): gate Send button on agent r..." | Re-trigger Greptile |
a5ac890 to
76e128f
Compare
Contributor
Prompt To Fix All With AIThis is a comment left during a code review.
Path: apps/code/src/renderer/features/sessions/service/service.test.ts
Line: 469-474
Comment:
**Fragile timing-based negative assertion**
This test waits a hard-coded 100 ms and then asserts `updateSession` was *never* called. If the async hydration callback takes longer (slow CI, high load), the assertion races past before the call happens and the test passes for the wrong reason.
A more reliable pattern is to wait on a positive side-effect that proves hydration completed (e.g. `vi.waitFor(() => expect(mockTrpcLogs.writeLocalLogs.mutate).toHaveBeenCalled())`) and only then check the negative:
```ts
// Wait for hydration to finish via a side-effect we know runs after the
// run_started handler (e.g. the log-write that happens unconditionally).
await vi.waitFor(() =>
expect(mockTrpcLogs.writeLocalLogs.mutate).toHaveBeenCalled(),
);
expect(mockSessionStoreSetters.updateSession).not.toHaveBeenCalledWith(
"run-123",
{ status: "connected" },
);
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: apps/code/src/renderer/features/sessions/components/mergeConversationItems.ts
Line: 193-199
Comment:
**Content-based dedup can suppress a legitimate follow-up with the same text**
While `optimisticItems` is non-empty, *every* `user_message` in `conversationItems` whose `content` matches the optimistic set is filtered — not just the first echo. If the user sends a second message with the exact same wording as the seeded optimistic item (e.g. task description "build me a thing" submitted again), its echo would also be silently removed from the view.
This window is narrow (only while the initial optimistic item persists), so it mainly matters if `clearOptimisticItems` / `replaceOptimisticWithEvent` is not called promptly once the first `session/prompt` echo lands. Worth verifying the cloud session has a reliable path to drain `optimisticItems` after the first echo is confirmed.
How can I resolve this? If you propose a fix, please make it concise.Reviews (2): Last reviewed commit: "fix(code): seed cloud optimistic when on..." | Re-trigger Greptile |
Wire up the long-declared `_posthog/run_started` notification after the ACP session is fully initialized. Documented in `acp-extensions.ts` as the canonical "agent is up and accepting user messages" handshake. Persisted to the session log so warm reconnects (sandbox restart with snapshot resume) replay it. Adapter-agnostic — emitted at the server layer rather than per-adapter. Generated-By: PostHog Code Task-Id: 8228d7eb-50f0-4148-bbc3-d47617e982f7
Cloud follow-up messages typed during sandbox setup, sandbox death, or restore could be silently lost: the dispatcher's old retry-loop drained the queue up-front, held the prompt in a local var, and on retry exhaustion surfaced a generic toast without re-enqueuing. Multiple auto-flush triggers also raced with the agent's initial `prompt()` call, producing `stopReason: cancelled` on otherwise-good user messages. - Dispatcher refactored to peek-and-confirm: drain → send → re-prepend on failure. Per-taskId re-entrance guard (`dispatchingCloudQueues`) prevents two concurrent triggers from double-dispatching. - `_posthog/run_started` flips `session.status` to `"connected"` (the explicit agent-ready handshake from #1). - `_posthog/turn_complete` is the only queue-drain trigger now — it's the safe boundary, fires when the agent has actually finished a turn. - `sendCloudPrompt` queues if `status !== "connected"` — covers the initial-boot window and sandbox restart/restore window with one signal instead of the previous optimistic-pending heuristic. - Removed the cloudStatus="in_progress" and post-log `!isPromptPending` auto-flushes; both raced with `sendInitialTaskMessage`. - `dequeueMessages` now reads the frozen committed state before entering the immer draft. Drained items used to be revoked proxies that crashed `combineQueuedCloudPrompts` once the setState callback exited — the silent root cause of "queue clears, message lost". - `prependQueuedMessages` setter rolls the queue back when a dispatch fails so the next trigger retries. Generated-By: PostHog Code Task-Id: 8228d7eb-50f0-4148-bbc3-d47617e982f7
Render the optimistic user-message bubble immediately on cloud task creation instead of waiting for the agent to echo it back via SSE (which can be many seconds while the sandbox provisions, clones, and boots). The optimistic seed itself is plumbed in #2 — this commit makes it visible in the right place. - ConversationView pins optimistic items above conversationItems for cloud sessions and content-dedups the agent's eventual echo so the bubble doesn't disappear-then-reappear when the real `session/prompt` event lands. Local sessions are unchanged: optimistic stays at chronological end and `replaceOptimisticWithEvent` swaps in place. - `useSessionConnection` plumbs `task.description` into `watchCloudTask` so the seed has content. Generated-By: PostHog Code Task-Id: 8228d7eb-50f0-4148-bbc3-d47617e982f7
Extracts the cloud-vs-local item merge logic from ConversationView into a pure helper so it can be unit-tested without rendering. Adds coverage for the run_started lifecycle notification, queue dispatch reliability (immer-proxy revocation, rollback on failure, status-gated send), and hydrate-time optimistic seeding. Generated-By: PostHog Code Task-Id: 8228d7eb-50f0-4148-bbc3-d47617e982f7
The hydrate-time seed only fired when persisted log entries were literally empty. By the time the hydrate fetch resolves, the agent has usually already emitted `_posthog/run_started` and setup-progress notifications, so a brand-new task with no user prompt yet would skip seeding and the task description would no longer be pinned at the top. Switch the gate to "no `session/prompt` request in events" — covers both the empty-log case and the lifecycle-notifications-only case. Generated-By: PostHog Code Task-Id: 8228d7eb-50f0-4148-bbc3-d47617e982f7
05c8979 to
d794969
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Cloud follow-up messages typed during sandbox setup, death, or restore could be silently lost — a generic error toast and the prompt vanished. Users also had no visual anchor for the prompt they typed at task creation until the agent eventually echoed it back via SSE (many seconds while the sandbox provisions).
The deeper framing: local runs already had a working version of all of this. This PR closes the parity gap.
Local–cloud parity at a glance
agent.reconnect.mutate()resolving setssession.status: "connected"cloudStatus+isPromptPending)_posthog/run_startedflipssession.status: "connected"sendLocalPromptcheckssession.statusupfront, throws if not connectedcloudStatus/isPromptPending/ optimistic-pending heuristic — racysendCloudPromptqueues ifsession.status !== "connected"(same shape as local)_posthog/turn_completeprependQueuedMessagesrolls the queue backappendOptimisticItem+replaceOptimisticWithEventswap in placehydrateCloudTaskSessionFromLogs, pinned at top, content-deduped against echoChanges
chore(agent): emit_posthog/run_started— the long-declared lifecycle handshake, finally wired up. Persisted to the log so warm reconnects replay it.fix(code): reliable queue dispatch — peek-and-confirm dispatcher, singleturn_completedrain trigger,session.status === "connected"queue gate, plus an immer-proxy fix indequeueMessagesthat was the silent root cause behind "queue clears, message disappears".feat(code): pin prompt at top during sandbox setup — optimistic bubble seeded only when there's no prior history, pinned above the conversation for cloud, content-deduped against the agent's echo. Local sessions untouched.Showcase
code-queue.mov
Test plan
Created with PostHog Code